﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using WU.LoadTester.Lib.Service;

namespace WU.LoadTester.Lib
{
    public class Program
    {

        public Data Data
        {
            get
            {
                return this._data;
            }
        }

        private Data _data;
        private Endpoint _service;
        private Settings _settings;


        /// <summary>
        /// Load the service endpoint
        /// </summary>
        public void LoadService(string url, BindingType binding)
        {
            _service = new Endpoint(url, _settings.FavoriteEditorPath, binding);
        }

        /// <summary>
        /// Load a web client
        /// </summary>
        public void LoadWebClient(string url, AuthSettings authSettings)
        {
            _service = new Endpoint(url, _settings.FavoriteEditorPath, authSettings);
        }

        public bool TestAuthenticationSettings(string url, AuthSettings authSettings)
        {
            var basicRequest = (HttpWebRequest)WebRequest.Create(url);
            basicRequest.ContentType = "application/json";
            basicRequest.Headers.Add("x-li-format", "json");
            basicRequest.Method = "GET";

            AuthenticationHelper.SetCredentials(ref basicRequest, authSettings, url);
            var response =  (HttpWebResponse) basicRequest.GetResponse();
            return response.StatusCode == HttpStatusCode.OK;
        }

        /// <summary>
        /// Start testing with the specified settings
        /// </summary>
        /// <param name="settings">Test settings</param>
        public void Load(Settings settings)
        {
            try
            {
                //Load data and create log file
                _data = new Data(settings.CSVFilepath);
                int length = _data.Matrix.Select(str => str.Aggregate((s1, s2) => s1 + s2).Length).Concat(new[] {0}).Max();
                if (settings.Logging)
                {
                    new LogFile(settings.LogFilePath, length);
                }
                else
                {
                    new LogFile(null, length);
                }
            }
            catch(Exception ex)
            {
               throw new Exception(Resources.Error_FileNotCreated,
                        ex);
            }
            _settings = settings;
            LogFile.DumpHeader(_settings);
    }
    public void Execute(Action<int> incrementProgressDelegate, CancellationToken token)
    {
            //A list of task is built with the specified maximum threads
            //Data is splitted between all tasks and executed in parallell
            var tasks = new List<Task>();
            int maxThreads = 0;
            int maxThreads2 = 0;
            ThreadPool.GetMaxThreads(out maxThreads, out maxThreads2);
            ThreadPool.SetMaxThreads((maxThreads + _settings.Threads), (maxThreads2 + _settings.Threads));

            //Load all tasks
            for(int i = 0; i < _settings.Threads; i++)
            {
                var rows = _data.Matrix.GetRange(i, (_data.Matrix.Count / _settings.Threads));
                var task = ExecuteCallAsync(rows, 1, token, incrementProgressDelegate);
                tasks.Add(task);
            }

            //Wait for all task to finish
            Task.WaitAll(tasks.ToArray());
        }

        private async Task ExecuteCallAsync(IEnumerable<string[]> rows, int percentage, CancellationToken token, Action<int> increment)
        {
            if (_settings.CallType == Settings.CallsType.Entire)
            {
                foreach (var row in rows)
                {
                    try
                    {
                        if (_settings.ClientType == Settings.ClientsType.Soap)
                        {
                            var result = await _service.CallAsync(row[0], row.Skip(1));
                            LogFile.WriteResult(row[0], row.Skip(1), result);
                        }
                        else
                        {
                            var result = await _service.WebCall(row[0], row.Skip(1));
                            LogFile.WriteResult(row[0], row.Skip(1), result);
                        }
                    }
                    catch (Exception callException)
                    {
                        LogFile.WriteEx(callException);
                    }

                    if (token.IsCancellationRequested)
                        return;

                    if (increment != null)
                        increment.Invoke(percentage);
                }
            }
            else
            {
                //Do random row on fixed on unlimited calls
                if (_settings.Calls == -1)
                    _settings.Calls = int.MaxValue;

                int calls = _settings.Calls / _settings.Threads;
                var rnd = new Random();

                for (int i = 0; i < calls; i++)
                {
                    var row = rows.ElementAt(rnd.Next(rows.Count() - 1));
                    try
                    {
                        if (_settings.ClientType == Settings.ClientsType.Soap)
                        {
                            var result = await _service.CallAsync(row[0], row.Skip(1));
                            LogFile.WriteResult(row[0], row.Skip(1), result);
                        }
                        else
                        {
                            var result = await _service.WebCall(row[0], row.Skip(1));
                            LogFile.WriteResult(row[0], row.Skip(1), result);
                        }
                    }
                    catch (Exception callException)
                    {
                        LogFile.WriteEx(callException);
                    }

                    if (token.IsCancellationRequested)
                        return;

                    if (increment != null)
                        increment.Invoke(percentage);
                }
            }
        }

        public void DisplayResults()
        {
            Process.Start(_settings.FavoriteEditorPath, LogFile.Path);
        }
    }
}
